package xyz.xtt.exchange.plus;

import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import com.baomidou.mybatisplus.core.toolkit.StringPool;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.db.sql.SqlFormatter;
import lombok.extern.slf4j.Slf4j;

@Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }),
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class,
				BoundSql.class }),
		@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
@Slf4j
public class SqlInterceptor implements Interceptor {

	/**
	 * 默认替换字符
	 */
	public static final String UNKNOWN = "UNKNOWN";
	/**
	 * 替换sql中的？占位符
	 */
	public static final String SQL_PLACEHOLDER = "#{%s}";

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		String completeSql = "";
		try {
			completeSql = getCompleteSqlInfo(invocation);
		} catch (RuntimeException e) {
			log.error("获取sql信息出错，异常信息 ", e);
		} finally {
			log.info("sql执行信息：[{}] ", completeSql);
		}
		Object proceed = invocation.proceed();
		return proceed;
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	/**
	 * 获取完整的sql信息
	 * 
	 * @param invocation
	 * @return
	 */
	private String getCompleteSqlInfo(Invocation invocation) {
		// invocation中的Args数组中第一个参数即为MappedStatement对象
		MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
		SqlCommandType sct = mappedStatement.getSqlCommandType();
		if (Objects.equals(sct, SqlCommandType.UPDATE)) {
		}
		// invocation中的Args数组中第二个参数为sql语句所需要的参数
		Object parameter = null;
		if (invocation.getArgs().length > 1) {
			parameter = invocation.getArgs()[1];
		}
		return generateCompleteSql(mappedStatement, parameter);
	}

	private String generateCompleteSql(MappedStatement mappedStatement, Object parameter) {
		// 获取sql语句
		String mappedStatementId = mappedStatement.getId();
		// BoundSql就是封装myBatis最终产生的sql类
		BoundSql boundSql = mappedStatement.getBoundSql(parameter);
		// 格式化sql信息
		String sql = SqlFormatter.format(boundSql.getSql());
		// 获取参数列表
		List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
		Object parameterObject = boundSql.getParameterObject();
		Configuration configuration = mappedStatement.getConfiguration();

		if (!CollUtil.isEmpty(parameterMappings) && parameterObject != null) {
			// 遍历参数完成对占位符的替换处理
			for (int i = 0; i < parameterMappings.size(); i++) {
				String replacePlaceHolder = String.format(SQL_PLACEHOLDER, i);
				sql = sql.replaceFirst("\\?", replacePlaceHolder);
			}
			// MetaObject主要是封装了originalObject对象，提供了get和set的方法用于获取和设置originalObject的属性值
			MetaObject metaObject = configuration.newMetaObject(parameterObject);
			for (int i = 0; i < parameterMappings.size(); i++) {
				ParameterMapping parameterMapping = parameterMappings.get(i);
				String replacePlaceHolder = String.format(SQL_PLACEHOLDER, i);
				String propertyName = parameterMapping.getProperty();
				if (metaObject.hasGetter(propertyName)) {
					Object obj = metaObject.getValue(propertyName);
					sql = sql.replaceFirst(Pattern.quote(replacePlaceHolder), Matcher.quoteReplacement(getParameterValue(obj)));
				} else if (boundSql.hasAdditionalParameter(propertyName)) {
					// 处理动态sql标签信息
					Object obj = boundSql.getAdditionalParameter(propertyName);
					sql = sql.replaceFirst(Pattern.quote(replacePlaceHolder), Matcher.quoteReplacement(getParameterValue(obj)));
				} else {
					// 未知参数，替换？为特定字符
					sql = sql.replaceFirst(Pattern.quote(replacePlaceHolder), UNKNOWN);
				}
			}
		}

		StringBuilder formatSql = new StringBuilder()
				.append(" mappedStatementId - ID：")
				.append(mappedStatementId)
				.append(StringPool.NEWLINE)
				.append("Execute SQL：")
				.append(sql);
		return formatSql.toString();
	}

	/**
	 *
	 * @author 毅航
	 * @date 2024/7/7 9:14
	 */
	private static String getParameterValue(Object obj) {
		// 直接返回空字符串将避免在 SQL 查询中加入不必要的单引号，从而保持查询的正确性。
		if (obj == null) {
			return "";
		}

		String stringValue = obj.toString();
		// 对于非空字符串，我们添加单引号以满足以满足参数优化的需求。
		return "'" + stringValue + "'";
	}
}
